#include <iostream>

#include <QtGui>
#include <GL/glew.h>
#include <QGLWidget>
#include <GL/glut.h>

#include <iostream>
#include <vector>

#include "Timer/timer.01.h"

#define SQR(x) ((x)*(x))

using namespace std;

class Object {
	public:
		long double h;
		long double x;
		long double v;
		long double a;
		long double m;
};

long double g = -1e-17;

class Visualizator: public QGLWidget {

	public:
		
		float timeStep;
		float timeFlag;
		
		Timer wallTime;
		int glTimerID, fpsTimerID;
		
		int frames;
		double framesTime;
		double fps;
		
		long double Etot0;
		
		vector<Object> o;
		
		Visualizator(QWidget *parent=0): QGLWidget(parent),
		timeStep(57), // [ms]
		frames(0),
		framesTime(0),
		fps(0)
		{ 
			
			uint N=2;
			long double h = 1.0/(5*N);
			o.resize(N);
			
			for (uint i=0; i<N; i++) {
				o[i].x = i/(long double)N*0.5+0.5;
				o[i].h = h;
				o[i].v = 0;
				o[i].a = g;
				o[i].m = 1;
			}
			
			
			for (uint i=.8*(N-1); i<N; i++) {
				o[i].a = g;
			}
			
			long double Epot0(0), Ekin0(0);
			for (uint i=0; i<N; i++) Ekin0+= .5*o[i].m*o[i].v*o[i].v;
			for (uint i=0; i<N; i++) Epot0+= -o[i].m*o[i].x*g;
			Etot0 = Ekin0 +  Epot0;
			
			setAutoFillBackground(false);
			wallTime.set();
			wallTime.sin = 0.001*timeStep;
			glTimerID = startTimer(timeStep);
			fpsTimerID = startTimer(250); // fps info time
			timeFlag = true;
			
			thread.op = &o;
			thread.start();
			
		};
		
		
		void timerEvent(QTimerEvent *event) {
			
			if (event->timerId() == glTimerID) {
				
				wallTime.set();
				if (timeFlag) moveTime(wallTime.sin+wallTime.sout);
				updateGL();
				
				framesTime+=wallTime.sin+wallTime.sout;
				frames++;
				wallTime.get();
				
			}
			
			if (event->timerId() == fpsTimerID){
				fps = frames/framesTime;
				framesTime=frames=0;
			}
			
			
		}
		
		virtual void moveTime(double dt){
			//printf("o[0].x = %g\n", o[0].x);
		}
		
		
		class MyThread : public QThread {
			public:
				
				vector<Object> *op;
				bool exitF;
				void run(){
					
					vector<Object> &o = *op;
					exitF = false;
					uint N = o.size();
					double Cr = 1;
					
					do {
						

						
						for (uint i=0; i<N; i++) {
							o[i].x += o[i].v + .5*o[i].a;
							o[i].v += o[i].a;
							
							/*
							// colision check
							for (uint j=0; j<i; j++) {
								
								double dx = o[i].x-o[j].x;
								double dx_min = 0.5*(o[i].h+o[j].h);
								
								if (fabs(dx)<dx_min) {
								
									o[i].x+=(dx_min-dx)*(1.000001);
									
									double vi = (Cr * o[j].m*(o[j].v-o[i].v) + o[i].m*o[i].v + o[j].m*o[j].v) / (o[i].m + o[j].m) ;
									double vj = (Cr * o[i].m*(o[i].v-o[j].v) + o[i].m*o[i].v + o[j].m*o[j].v) / (o[i].m + o[j].m) ;
									
									o[i].v = vi;
									o[j].v = vj;
									o[j].a = o[i].a;
									
								}
								
							}
							
							// bounding conditions
							
							if (o[i].x - .5*o[i].h < 0) {
								o[i].x = .5*o[i].h;
								o[i].v*=0;
							}
							
							if (1< o[i].x + .5*o[i].h) {
								o[i].x = 1 - .5*o[i].h;
								o[i].v*=-1;
							}
							*/	
						}
						

					} while (!exitF);
					
				}
				~MyThread(){ exitF = true; }		
		} thread;
		
		
		
		
		virtual void initializeGL() {  // Set up the rendering context, define display lists etc.:

			glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
			glShadeModel(GL_FLAT);
			glClearDepth(1.0);							// Enables Clearing Of The Depth Buffer
			glDepthFunc(GL_LESS);
			glEnable(GL_DEPTH_TEST);					// Enables Depth Testing
			glEnable(GL_CULL_FACE);
			
		}

		void resizeGL(int w, int h){  // setup viewport, projection etc.:
			glViewport(0, 0, w, h);
		}
		
		virtual void paintGL() {
			
			glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

			glMatrixMode ( GL_PROJECTION );
			glLoadIdentity(); // загружает еденичную матрицу
			
			float aspect = (float)width()/height();
			
			glOrtho(-aspect, +aspect, 0, 1, -1000, 1000);
			
			glMatrixMode(GL_MODELVIEW); 
			glLoadIdentity();
			
			
			glColor3f(0.7,0.6,0.3);
			for (uint i=0; i<o.size(); i++)
				glRectangle(o[i].x, o[i].h);	
		
			
			
			long double Epot(0), Ekin(0);
			for (uint i=0; i<o.size(); i++) Ekin+= .5*o[i].m*o[i].v*o[i].v;
			for (uint i=0; i<o.size(); i++) Epot+= -o[i].m*o[i].x*g;
			long double Etot = Ekin +  Epot;
			
			char str[256];
			
			sprintf(str, "% 5.1Le", (Etot-Etot0)/Etot0);
			setWindowTitle(str);
		}


	
		virtual void keyPressEvent(QKeyEvent *event){
			
			if (event->key() == Qt::Key_F) 
				setWindowState(windowState()^Qt::WindowFullScreen);
			
			if (event->key() == Qt::Key_Space)
				timeFlag = !timeFlag;
			
			if (event->key() == Qt::Key_Escape)
				if (windowState()) setWindowState(windowState()^Qt::WindowFullScreen);
				else exit(0);
				
		}
		
		
		void glRectangle(float y, float h) {
			
			float w = 1;
			
			glBegin(GL_QUADS);
			glVertex2f(-.5*w, y-.5*h);
			glVertex2f(+.5*w, y-.5*h);
			glVertex2f(+.5*w, y+.5*h);
			glVertex2f(-.5*w, y+.5*h);
			
			glEnd();	
		}
		
		void glPrint(int x, int y, const char *string, void *font) {
	
			// save current projection matrix
			glMatrixMode(GL_PROJECTION); 
			glPushMatrix();
	
			// get viewport size
			GLint viewport[4];
			glGetIntegerv(GL_VIEWPORT, viewport );
	
			// set projection matrix to ortho settings
			glLoadIdentity();
			glOrtho(0, viewport[2], viewport[3], 0, -1, 1);
	
			// save current modelview matrix
			glMatrixMode(GL_MODELVIEW); 
			glPushMatrix();
	
			//text drowing
			glLoadIdentity();

			int indent = 15; //
			glRasterPos2i(x, y);
			int len = (int) strlen(string);
			for (int i = 0; i < len; i++) {
		
				if (string[i] == '\n') {
					y+=indent;
					glRasterPos2i(x, y);
					continue;
				}
				glutBitmapCharacter(font, string[i]);
		
			}
	
			// restore previos settings
			glMatrixMode(GL_PROJECTION); 
			glPopMatrix();
	
			glMatrixMode(GL_MODELVIEW); 
			glPopMatrix();
			
		}
	
		
};



int main(int argc, char *argv[]) {
	
	glutInit(&argc, argv); //initialize glut
	QApplication app(argc, argv);
	
	Visualizator visualizator;
	visualizator.resize(200, 600);
	visualizator.setWindowTitle( QString("Visualizator Lite for 911") );
	visualizator.show();
	
	return app.exec();
	
	
}

